
--[[
*********************************************************************************
** 
** Copyright 2021-2024 LuaOS
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
** SOFTWARE.
** 
*********************************************************************************
]]--

----------------------------------------------------------------------------

local class   = require("luaos.classy")
local abs     = math.abs;
local random  = math.random;
local insert  = table.insert;

----------------------------------------------------------------------------

local delta = {
  { x = -1, y = -1 },
  { x = -1, y =  0 },
  { x = -1, y =  1 },
  { x =  0, y = -1 },
  { x =  0, y =  1 },
  { x =  1, y = -1 },
  { x =  1, y =  0 },
  { x =  1, y =  1 },
};

----------------------------------------------------------------------------

local _M = class("bitmap");

----------------------------------------------------------------------------

function _M:__init()
  self.map = std.bitmap();
  self:resize(0, 0, 0);
end

----------------------------------------------------------------------------

function _M:update()
  self._min_x = self:min_x();
  self._min_y = self:min_y();
  self._max_x = self:max_x();
  self._max_y = self:max_y();
end

----------------------------------------------------------------------------

function _M:load(filename)
  local ok, err = pcall(self.map.load, self.map, filename);
  if ok then self:update() end
  return ok, err;
end

----------------------------------------------------------------------------

function _M:resize(w, h, v)
  self.map:resize(w, h, v or 0);
  self:update();
end

----------------------------------------------------------------------------

function _M:save(filename)
  return pcall(self.map.save, self.map, filename);
end

----------------------------------------------------------------------------

function _M:max_i()
  return self.map:max_i();
end

----------------------------------------------------------------------------

function _M:min_i()
  return self.map:min_i();
end

----------------------------------------------------------------------------

function _M:max_x()
  return self.map:max_x();
end

----------------------------------------------------------------------------

function _M:min_x()
  return self.map:min_x();
end

----------------------------------------------------------------------------

function _M:max_y()
  return self.map:max_y();
end

----------------------------------------------------------------------------

function _M:min_y()
  return self.map:min_y();
end

----------------------------------------------------------------------------

function _M:width()
  return self.map:width();
end

----------------------------------------------------------------------------

function _M:height()
  return self.map:height();
end

----------------------------------------------------------------------------

function _M:size()
  return self.map:size();
end

----------------------------------------------------------------------------

function _M:index(x, y)
  return self.map:convert(x, y);
end

----------------------------------------------------------------------------

function _M:coord(i)
  return self.map:convert(i);
end

----------------------------------------------------------------------------

function _M:get(x, y)
  return self.map:get(x, y);
end

----------------------------------------------------------------------------

function _M:geti(i)
  return self.map:get(i);
end

----------------------------------------------------------------------------

function _M:set(x, y, v)
  return self.map:set(x, y, v);
end

----------------------------------------------------------------------------

function _M:seti(i, v)
  return self.map:set(i, v);
end

----------------------------------------------------------------------------

function _M:append(cx, cy, ex, ey, ii, gg, push_vertex)
  local dx = delta[ii].x;
  local dy = delta[ii].y;
  local nx = cx + dx;
  if nx < self._min_x or nx > self._max_x then
    return;
  end
  local ny = cy + dy;
  if ny < self._min_y or ny > self._max_y then
    return;
  end
  if self:get(nx, ny) == 1 then
    local nh = (abs(nx - ex) + abs(ny - ey)) * 10;
    local ng = gg + ((dx ~= 0 and dy ~= 0) and 14 or 10);
    push_vertex(self:index(nx, ny), ng, nh);
  end
end

----------------------------------------------------------------------------

function _M:neighbors(e, i, g, push_vertex)
  local cx, cy = self:coord(i);
  local ex, ey = self:coord(e);
  local nn = random(1, 8);
  for i = 1, 8 do
    local ii = (nn & 7) + 1;
    self:append(cx, cy, ex, ey, ii, g, push_vertex);
	nn = nn + 1
  end
end

----------------------------------------------------------------------------

function _M:navigate(x1, y1, x2, y2, depth)
  local start  = self:index(x1, y1);
  local finish = self:index(x2, y2);
  if self:geti(start) == 0 or self:geti(finish) == 0 then
    return nil;
  end
  depth = depth or 8192;
  local path = math.navigate(start, finish, depth, bind(self.neighbors, self));
  if not path then
    return nil;
  end
  local result = {};
  for _, i in ipairs(path) do
    local x, y = self:coord(i);
	insert(result, { x = x, y = y });
  end
  return result;
end

----------------------------------------------------------------------------

return _M;

----------------------------------------------------------------------------
